/**
 * @file s_weight.c
 * @brief 
 * @author xuesong (wenshaoliu@foxmail.com)
 * @version 1.0
 * @date 2023-12-05
 * 
 * @copyright Copyright (c) 2023  by xuesong
 * 
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2023-12-05     <td>1.0     <td>xuesong   <td>内容
 * </table>
 */

#include "s_weight.h"
#include "UserKvSave.h"
#include "crc16.h"
#include "Display.h"
#include "s_calibration.h"
#include "user_Bz.h"
//-------------------------------------------------------------
const uint16_t ScaleMinWeight[SCALE_RANGE_CONT] = {30,30,30};
const uint32_t ScaleMaxWeight[SCALE_RANGE_CONT] = {100100,210000,310000};
const uint32_t ScaleRang[SCALE_RANGE_CONT] = {100000,200000,300000};
// const uint32_t STAND_WEIGHT_POINT[CALIBRATE_PIONT] = {10000,50000,100000,200000,300000};	//100.00g,500.00g,1000.00g,2000.00g,3000.00g
const uint32_t STAND_WEIGHT_POINT[CALIBRATE_PIONT] = {10000,50000,100000,200000};	//100.00g,500.00g,1000.00g,2000.00g
uint32_t StandWeightCalibration[CALIBRATE_PIONT];
Weight_t ScaleWeight={
	.Flag.Byte = 0,
};
Scale_data_t ScaleInfor;
//------------------------------------------------------------------------
void InitScale(void)
{
    bool ScaleCalOK = false;
	uint16_t crcCheck = 0;
	uint8_t  index = 0;
	uint32_t SubStandWeight = 0;
    Scale_data_t  tempScaleData;
	ReadScaleInfor(&tempScaleData);
	if(tempScaleData.Existflag == EXSIT_FLAG)
	{
		crcCheck = crc16(0xFFFF,(uint8*)&tempScaleData,sizeof(Scale_data_t)-sizeof(tempScaleData.Crccheck_sum));
        LOG("read Scale Infor:");
        displayScaleInfor(&tempScaleData);
        LOG("Check sum = %d,now = %d\r\n",tempScaleData.Crccheck_sum,crcCheck);
        if(crcCheck == tempScaleData.Crccheck_sum)
        {
            ScaleCalOK = TRUE;
			for(index = 0; index < CALIBRATE_PIONT; index++)
			{
				SubStandWeight = STAND_WEIGHT_POINT[index];
				if(index)
					SubStandWeight -= STAND_WEIGHT_POINT[index-1];

				if(SubStandWeight*CAL_ADCSUB_MUL_MIN <= tempScaleData.Calibration[index] && SubStandWeight*CAL_ADCSUB_MUL_MAX >= tempScaleData.Calibration[index])
					continue;
				ScaleCalOK = false;
				break;				
			}
        }
	}

	if(ScaleCalOK)
	{
		for(index = 0; index < CALIBRATE_PIONT; index++)
		{
			StandWeightCalibration[index] = tempScaleData.Calibration[index];
		}
		osal_memcpy(&ScaleInfor,&tempScaleData,sizeof(tempScaleData));
	}else{
		for(index = 0; index < CALIBRATE_PIONT; index++)
		{
			SubStandWeight = STAND_WEIGHT_POINT[index];
			if(index)
				SubStandWeight -= STAND_WEIGHT_POINT[index-1];
			StandWeightCalibration[index] = SubStandWeight*CAL_ADCSUB_MUL_MIN*6;
			ScaleInfor.Calibration[index] = StandWeightCalibration[index];
		}	
		ScaleInfor.MaxWeight = ScaleMaxWeight[0];
		ScaleInfor.MinWeight = ScaleMinWeight[0];
	}
	ScaleWeight.DivisionAdc = StandWeightCalibration[0]*5/STAND_WEIGHT_POINT[0];		//0.5D
	LOG("weight max=%d,min=%d,divisionadc=%d\r\n",ScaleInfor.MaxWeight,ScaleInfor.MinWeight,ScaleWeight.DivisionAdc);
}

int32_t CalculateWeight(uint32_t adc,const uint32_t *StandWeight,const uint32_t *SubSample,uint8_t Steps)
{
	int32_t weight = 0;
	uint32_t SubStandWeight = 0;
	uint8_t index = 0;
    uint64_t tempData = 0;
	for(index = 0; index < Steps ; index++)
	{
		SubStandWeight = StandWeight[index];
		if(index)
			SubStandWeight -= StandWeight[index-1];
		if(adc >= SubSample[index])
		{
			weight += SubStandWeight;
			adc -= SubSample[index];
		}else{
			break;
		}
	}
	if(index >= Steps)
		index = Steps - 1;

	tempData = adc;
	tempData *= SubStandWeight;
	tempData /= SubSample[index];
	return weight + tempData;	
}

void CalculateWeightProcess(void)
{
    bool negtive = false;
    int32_t adc_change = 0;
    int32_t weight = 0;
    User_BZ_t bzData;
	ScaleWeight.Flag.Bit.WtNew = false;
	ScaleWeight.Flag.Bit.Lock = false;
	
	if(userAdcStruct.Flag.Bit.SampleNew && !ScaleWeight.Flag.Bit.WtEnable)
    	LOG("adc o=0x%x,F=0x%X,d=0x%X,z=0x%X,T=0x%X\n",userAdcStruct.Adc,userAdcStruct.FilterAdc,userAdcStruct.DisplayAdc,ScaleWeight.ZeroPointADC,ScaleWeight.TarePointADC);
	
	if(!ScaleWeight.Flag.Bit.WtEnable)
		return;
	if(ScaleWeight.Flag.Bit.tare)
	{
		if(userAdcStruct.Flag.Bit.lock || (userAdcStruct.Flag.Bit.locked && ScaleWeight.Flag.Bit.Atuotare))
		{
			ScaleWeight.Flag.Bit.Atuotare = FALSE;
			ScaleWeight.Flag.Bit.tare = false;
			ScaleWeight.TarePointADC = userAdcStruct.DisplayAdc;
			LOG("trae adc = %x\r\n",ScaleWeight.TarePointADC);
			// if(ScaleWeight.Flag.Bit.TareNeedBZ)
			{
				ScaleWeight.Flag.Bit.TareNeedBZ = FALSE;
				osal_memset(&bzData,0,sizeof(bzData));
				bzData.flag = B_BZ_RUN;
				bzData.Count = 1;
				// bzData.OnTime = 1000;   //1.5s
				Bz_init_ONOFF(bzData);
			}
		}
		// else{
		// 	ScaleWeight.NetWeight = 0;
		// 	ScaleWeight.Flag.Bit.Load = false;
		// 	ScaleWeight.Flag.Bit.Lock = false;
		// 	ScaleWeight.Flag.Bit.WtNew = TRUE;
		// }
		// return;
	}
	if(!userAdcStruct.Flag.Bit.SampleNew)
		return;

	F_LoadTraceZero();

	if(userAdcStruct.Flag.Bit.lock)
		ScaleWeight.Flag.Bit.Lock = TRUE;
	ScaleWeight.Flag.Bit.WtNew = TRUE;
	adc_change = userAdcStruct.DisplayAdc - ScaleWeight.ZeroPointADC;
    if(adc_change < 0)
    {
        negtive = TRUE;
        adc_change = 0 - adc_change;
    }

    // LOG("caculate adc = %x\r\n",adc_change);
    weight = CalculateWeight(adc_change,STAND_WEIGHT_POINT,StandWeightCalibration,CALIBRATE_PIONT);

	ScaleWeight.Flag.Bit.overLoad = false;
	if(weight > ScaleInfor.MaxWeight)
		ScaleWeight.Flag.Bit.overLoad = true;
	// if(RoundDiv(weight,10) < (ScaleInfor.MinWeight/10))
    // {
    //     weight = 0;
    // }

    if(negtive)
        weight = 0 - weight;        // complement code save

    ScaleWeight.GrossWeight = weight;


    negtive = false;
	adc_change = ScaleWeight.TarePointADC - ScaleWeight.ZeroPointADC;
    if(adc_change < 0)
    {
        negtive = TRUE;
        adc_change = 0 - adc_change;
    }
    weight = CalculateWeight(adc_change,STAND_WEIGHT_POINT,StandWeightCalibration,CALIBRATE_PIONT);

	ScaleWeight.Flag.Bit.tared = false;
	if(weight >= ScaleInfor.MinWeight*10)
	{
		ScaleWeight.Flag.Bit.tared = TRUE;
	}

    if(negtive)
        weight = 0 - weight;        // complement code save
	
    ScaleWeight.NetWeight = ScaleWeight.GrossWeight - weight;
	LOG("Weight o-Net =%d,T =%d,g =%d,",ScaleWeight.NetWeight,weight,ScaleWeight.GrossWeight);
	// if(ScaleWeight.Flag.Bit.Lock)
	// {
	// 	LOG("rofe weight = 0x%x,tare weight=0x%x\n",ScaleWeight.GrossWeight,weight);
	// 	LOG("adc = 0x%x\n",userAdcStruct.DisplayAdc);
	// }
	if(RoundDiv(ABS(ScaleWeight.NetWeight),10) >= (ScaleInfor.MinWeight/10))
	{
		ScaleWeight.Flag.Bit.Load = true;
	}else{
		ScaleWeight.NetWeight = 0;
		ScaleWeight.Flag.Bit.Load = false;
	}
	if(ABS(ScaleWeight.NetWeight) > ScaleInfor.MaxWeight)
	{
		ScaleWeight.Flag.Bit.overLoad = true;
		ScaleWeight.NetWeight = ScaleInfor.MaxWeight;
	}

	if(ScaleWeight.Flag.Bit.Lock)
	{
		adc_change = userAdcStruct.DisplayAdc - ScaleWeight.TarePointADC;
		negtive = false;
		if(adc_change < 0)
		{
			negtive = true;
			adc_change = 0 - adc_change;
		}
		weight = ScaleInfor.MinWeight;
		weight *= StandWeightCalibration[0];
		weight /= STAND_WEIGHT_POINT[0];
		weight /= 10;							//0.1minWeightAdc
		if(adc_change < weight*3)
		{
			;
		}else if(adc_change > weight*5)
		{
			adc_change = 0;						//不追零防止起不来
		}else{
			adc_change = weight*3;				//只追0.5 防止太快起不来
		}
		if(adc_change != 0)
		{
			if(negtive)
				adc_change = 0 - adc_change;
			ScaleWeight.TarePointADC += adc_change;
			if(!ScaleWeight.Flag.Bit.Load && ScaleWeight.GrossWeight < ScaleInfor.MinWeight)
			{
				ScaleWeight.ZeroPointADC = ScaleWeight.TarePointADC;
			}
			// LOG("Zero=0x%X,Tare=0x%x\n",ScaleWeight.ZeroPointADC,ScaleWeight.TarePointADC);
		}
	}

	if(!ScaleWeight.Flag.Bit.Load)
		ScaleWeight.Flag.Bit.Lock = false;	
	else{												//100g整数靠拢 0.1g
		negtive = FALSE;
		if(ScaleWeight.NetWeight < 0)
		{
			negtive = TRUE;
			ScaleWeight.NetWeight = 0 - ScaleWeight.NetWeight;
		}
		if((ScaleWeight.NetWeight/10000) <= 1)			//100.00g附近
		{
			uint16_t remainder = ScaleWeight.NetWeight%10000;
			if(remainder < 17)	//0.15g
			{
				ScaleWeight.NetWeight -= remainder;
			}else{
				remainder = 10000 - remainder;
				if(remainder < 17)
					ScaleWeight.NetWeight += remainder;
			}
		}
		if(negtive)
		{
			ScaleWeight.NetWeight = 0 - ScaleWeight.NetWeight;
		}
	}
    LOG("Netweight =%d, adc o=0x%x,F=0x%X,d=0x%X,z=0x%X,T=0x%X\n",ScaleWeight.NetWeight,userAdcStruct.Adc,userAdcStruct.FilterAdc,userAdcStruct.DisplayAdc,ScaleWeight.ZeroPointADC,ScaleWeight.TarePointADC);
}

void F_LoadTraceZero(void)
{
    bool negtive = false;
	static uint8_t  TraceZeroCnt = 0;
	static bool lastDerction = false;
	static uint32_t LockedDisplayAdc = 0;
    int32_t adc_change = 0;

	if(userAdcStruct.Flag.Bit.locked)
	{
		// if(LockedDisplayAdc == 0)			// Frist lock adc Flash Adc
		// 	LockedDisplayAdc = userAdcStruct.DisplayAdc;
		if(userAdcStruct.Flag.Bit.lock)
		{
			if(LockedDisplayAdc == 0)
				LockedDisplayAdc = userAdcStruct.FilterAdc;	
			else{
				adc_change = LockedDisplayAdc - userAdcStruct.FilterAdc;
				if(adc_change < 0)
					adc_change = 0 - adc_change;
				if(adc_change >= (ScaleWeight.DivisionAdc*2*7/10))	// 0.7D
					LockedDisplayAdc = userAdcStruct.FilterAdc;
			}
		}		
	}else{
		LockedDisplayAdc = 0;
	}

	adc_change = LockedDisplayAdc - userAdcStruct.FilterAdc;
	// adc_change = userAdcStruct.DisplayAdc - userAdcStruct.FilterAdc;
	negtive = false;
	if(adc_change < 0)
	{
		negtive = true;
		adc_change = 0 - adc_change;
	}
	if(negtive != lastDerction)
		TraceZeroCnt = 0;
	if(ScaleWeight.Flag.Bit.Load && adc_change < ScaleWeight.DivisionAdc)		//0.5D
	{
		TraceZeroCnt++;
		// if(TraceZeroCnt > 20)//15)
		if(TraceZeroCnt > 15)
		{
			if(adc_change > (ScaleWeight.DivisionAdc*2*3/10))					//0.3D
			{
				adc_change = ScaleWeight.DivisionAdc*2*3/10;
				TraceZeroCnt -= 7;
			}else{
				TraceZeroCnt -= 10;
			}
			if(negtive)
			{
				adc_change = 0 - adc_change;
			}
			userAdcStruct.DisplayAdc -= adc_change;
			ScaleWeight.TarePointADC -= adc_change;
			LockedDisplayAdc -= adc_change;
		}
	}else{
		TraceZeroCnt = 0;
	}	
	lastDerction = negtive;
}

//----------------------------------------------------------------
/**
 * @brief  四舍五入除法
 * @param  dividend      	被除数
 * @param  divisor          除数
 * @return uint64_t 
 */
uint64_t RoundDiv(uint64_t dividend,uint32_t divisor)
{
	dividend <<= 1;
	dividend /= divisor;
	dividend ++;
	return dividend>>1;
}
/**
 * @brief 4舍5入单位转换 0.01g --> 0.01oz
 * @param  Gweight          0.01g
 * @return uint32_t 		0.01oz
 */
uint32_t Weightg2oz(uint32_t Gweight)
{
	uint64_t oz_weight = Gweight;
	oz_weight *= 35274;
	return (RoundDiv(oz_weight,1000000));
}
//----------------------------------------------------------------
void ClrWeightDisplayBuffer(Display_t *p_display)
{
	for(uint8_t i = 0;i<sizeof(p_display->numberDataMidle);i++)
		p_display->numberDataMidle[i] = ' ';
	p_display->SymbleFlag.bit.g = false;
	p_display->SymbleFlag.bit.oz = false;
	p_display->SymbleFlag.bit.negtiv = false;
	p_display->SymbleFlag.bit.dp1 = false;
	p_display->SymbleFlag.bit.dp2 = false;
	p_display->SymbleFlag.bit.dp3 = false;
	p_display->SymbleFlag.bit.dp4 = false;
}
/**
 * @brief 
 * @param  weight           0.01g
 * @param  weightUnit       MyParamDoc
 */
void displayWeight(int32_t weight,uint8_t weightUnit)
{
	bool negative = false;
	uint32_t displayValue;
	ClrWeightDisplayBuffer(&DisplayData);
	if(ScaleWeight.Flag.Bit.overLoad)
	{
		displayChar2Tuble(" Err ",DisplayData.numberDataMidle,5);
	}else{		
		if(weight < 0)
		{
			negative = true;
			DisplayData.SymbleFlag.bit.negtiv = true;
			weight = 0 - weight;
		}
		switch(weightUnit)
		{
			case	WEIGHT_UNIT_OZ:
					{
						displayValue = Weightg2oz(weight);
						DisplayData.SymbleFlag.bit.oz = true;
						if(displayValue >= 20000)
						{
							displayValue = RoundDiv(displayValue,10);
							displayNumber2Number(displayValue,1,DisplayData.numberDataMidle,5);
							DisplayData.SymbleFlag.bit.dp1 = true;
						}else{
							displayNumber2Number(displayValue,2,DisplayData.numberDataMidle,5);
							DisplayData.SymbleFlag.bit.dp2 = true;
						}
					}
					break;
			case	WEIGHT_UNIT_G:
			default:
						displayValue = RoundDiv(weight,10);
						DisplayData.SymbleFlag.bit.g = true;
						if(displayValue >= 20000)
						{
							displayValue = RoundDiv(displayValue,10);
							displayNumber2Number(displayValue,0,DisplayData.numberDataMidle,5);
						}else{
							displayNumber2Number(displayValue,1,DisplayData.numberDataMidle,5);
							DisplayData.SymbleFlag.bit.dp1 = true;
						}
					break;
		}

		if(negative)
			DisplayData.SymbleFlag.bit.negtiv = true;
	}
}




